package com.alex;

import com.alex.support.CustomConnectionKeepAliveStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * RestTemplate
 *  1. 配置restTemplate相关连接池.
 *  2. 关于, restTemplate获取数据, 带泛型如何解决.
 *      1. restTemplate得到数据时, 通过Json无法解析数据时, 则会返回LinkedHashMap.
 *
 *
 * 配置Serializer.
 *
 * RestTemplate配置：
 *  1. 优化连接池的管理.
 *  2. 超时的设置, 不会因为下游的服务不可用, 导致此服务使用RestTemplate调用下游服务, 而长时间一直连接, 导致拖垮本服务.
 *
 * @author Alex.shu
 */
@Slf4j
@SpringBootApplication
public class RestTemplateApplication implements ApplicationRunner {

    @Autowired
    private RestTemplate restTemplate;

    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .sources(RestTemplateApplication.class)
                .bannerMode(Banner.Mode.OFF)
                .web(WebApplicationType.NONE)
                .run(args);
    }

    @Bean
    public HttpComponentsClientHttpRequestFactory requestFactory() {
        PoolingHttpClientConnectionManager connectionManager =
                // TTL
                new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        // 最大连接数
        connectionManager.setMaxTotal(200);

        // 每个route最大20个连接.
        connectionManager.setDefaultMaxPerRoute(20);

        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .evictIdleConnections(30, TimeUnit.SECONDS)
                // 默认是自动重试; 设置为禁止自动重试;
                .disableAutomaticRetries()
                // 默认是永久有效
//                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                // 自定义
                .setKeepAliveStrategy(new CustomConnectionKeepAliveStrategy())
                .build();

        return new HttpComponentsClientHttpRequestFactory(httpClient);
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                // 设置连接超时100毫秒
                .setConnectTimeout(Duration.ofMillis(100))
                .setReadTimeout(Duration.ofMillis(500))
                .requestFactory(this::requestFactory)
                .build();
    }

    @Override
    public void run(ApplicationArguments args) {
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:7777/user/name")
                .build().toUri();

        RequestEntity<Void> requestEntity = RequestEntity.get(uri)
                .accept(MediaType.TEXT_XML)
                .build();

        ResponseEntity<String> userName = restTemplate.exchange(requestEntity, String.class);
      log.warn("Response status: {}, headers: {}", userName.getStatusCode(), userName.getHeaders());
      log.warn("Response body: {}", userName.getBody());


        URI uriList = UriComponentsBuilder
                .fromUriString("http://localhost:7777/user/list")
                .build().toUri();

        // 无法直接使用List.class, 作为接受参数;
        ParameterizedTypeReference<List<String>> ptr = new ParameterizedTypeReference<List<String>>() {};

        ResponseEntity<List<String>> userNameGroup = restTemplate.exchange(uriList, HttpMethod.GET, null, ptr);
        Optional.ofNullable(userNameGroup).ifPresent(
                group -> group.getBody().forEach(item -> log.warn(item))
        );
    }
}
